﻿using System;
using System.Collections;
using System.Html;
using System.Runtime.CompilerServices;
using Awaiter;
using Nsb;
using Nsb.Classes;
using ThemeChain;
using jQueryApi;

namespace PlaySpace.Classes
{
    // ReSharper disable PossibleInvalidCastException

    [IgnoreNamespace]
    public static class Site
    {
        //public const string SendTo = "nsbawden@gmail.com";
        private const string OurEmail = "playspaces@playnexus.com";
        public const string SpacesPath = "../../spaces";
        public const string PasswordMatchMsg = "passwords don't match";
        public const string SpaceNameShortMsg = "space name must be at least 4 characters long";
        public const string SpaceNameLongMsg = "space name must be less than 20 characters long";
        public const string SpaceNameLowerMsg = "space name must be all lower case";
        public const string SpaceNameLettersMsg = "space name can only contain letters, numbers, and underscore and must start with a letter";
        public const string EmailValidMsg = "please enter a valid email address";
        public const string PasswordLengthMsg = "password must be at least 8 characters long";
        public const string IncorrectMathMsg = "incorrect math result";
        private static readonly string SendTo = (string) Window.LocalStorage.GetItem("DevEmailTo") ?? "nsbawden@gmail.com";
        public static DirMap Map;
        private static jQueryObject validateEl;

        [PreserveCase, PreserveName]
        public static void HomePage()
        {
            jQuery.Select("#CreateButton")
                .AddClass("DefaultButton")
                .Click(
                    delegate
                    {
                        try
                        {
                            CreateSpaceItem csi = new CreateSpaceItem();
                            csi.NameEl = jQuery.Select("#SpaceName");
                            csi.EmailEl = jQuery.Select("#Email");
                            csi.Password1El = jQuery.Select("#Password");
                            csi.Password2El = jQuery.Select("#PasswordAgain");

                            csi.Name = GigForms.ValidateThrow(csi.NameEl);
                            ValidateSpaceNameThrow(csi);

                            csi.Email = GigForms.ValidateThrow(csi.EmailEl);
                            ValidateEmailAddrThrow(csi);

                            csi.Password1 = GigForms.ValidateThrow(csi.Password1El);
                            ValidatePwThrow(csi);
                            csi.Password2 = GigForms.ValidateThrow(csi.Password2El);
                            if (csi.Password1 != csi.Password2)
                            {
                                GigForms.SetDeef(csi.Password1El);
                                GigForms.SetDeef(csi.Password2El);
                                Ask.Ok(PasswordMatchMsg);
                                return;
                            }
                            ValidateMath();

                            new Await()
                                .AddAw(VerifySpaceAvailableAw, csi.Name)
                                .AddAw(DomElement.RequireResultAw, true)
                                .AddDl(delegate(Await aw) { CreateSpaceAw(aw, csi); })
                                .Commit();
                        }
                        catch (Exception ex)
                        {
                            if (ex.Message != GigForms.ValidateException)
                                throw ex;
                        }
                    }
                )
                .Keydown(
                    delegate(jQueryEvent e)
                    {
                        if (e.Which == 9 /*tab*/)
                        {
                            jQuery.Select("#SpaceName").Focus();
                            DomElement.CancelEvent(e);
                        }
                    })
                ;

            CreateMath();
            GigForms.InitializePageInput();
            jQuery.Document
                .Bind("PageDefaultFocus", PageDefaultFocusEv)
                .Trigger("PageDefaultFocus")
                ;
        }

        public static void PageDefaultFocusEv(jQueryEvent e)
        {
            jQueryObject el = validateEl ?? jQuery.Select("#SpaceName");

            if (validateEl == null)
                jQuery.Select("input").Each(
                    delegate(int i, Element domEl)
                    {
                        jQueryObject t = jQuery.FromElement(domEl);
                        if (String.IsNullOrEmpty(t.GetValue().Trim()))
                        {
                            el = t;
                            return false;
                        }
                        return true;
                    });
            el.Plugin<Jewl>().ReFocus();
        }

        [PreserveName, PreserveCase]
        public static void SitesPage()
        {
            Spaces.Page();
        }

        public static jQueryEventHandler SpaceDelUx(string site)
        {
            int start = (new Date()).GetTime();
            return delegate(jQueryEvent e)
                   {
                       Await awx = new Await();
                       if (!e.AltKey)
                           awx
                               .AddAw(new Ask().OkCancelAw, new Dictionary("Msg", ThemeYoshi.ForSure))
                               .AddAw(DomElement.RequireResultAw, true);
                       awx
                           .AddDl(delegate(Await aw) { FileBlobBase.EmptyFolderAw(aw, Uri.Join(SpacesPath, site), true); })
                           .AddDx(delegate { SitesPage(); })
                           .AddDx(delegate { Exceptions.SaveLog("build", String.Format("deleted - space {0} time={1}ms", site, (new Date()).GetTime() - start)); })
                           .Commit();
                   };
        }

        private static void SendMeMail(CreateSpaceItem csi)
        {
            Dictionary mail = new Dictionary();
            SendMailData smd = (SendMailData) (object) mail;

            string href = Uri.AddPair("http://www.playnexus.com/spaces/" + csi.Name + "/" + ThemeBase.AppPath + "/activate.php", "act", csi.ActivateId);

            smd.to = SendTo;
            smd.subject = "New playspace created";
            smd.from = csi.Email;
            smd.content = "";
            smd.content += "Space name: " + csi.Name + "<br>";
            smd.content += "Space email: " + csi.Email + "<br>";
            smd.content += "<br>";
            smd.content += String.Format("Click this link to activate: <a href='{0}'>{1}</a>", href, href);
            GigForms.SendTheMail(mail, false);
        }

        private static void SendThemMail(CreateSpaceItem csi)
        {
            Dictionary mail = new Dictionary();
            SendMailData smd = (SendMailData) (object) mail;

            string href = Uri.AddPair("http://www.playnexus.com/spaces/" + csi.Name + "/" + ThemeBase.AppPath + "/activate.php", "act", csi.ActivateId);

            smd.to = csi.Email;
            smd.subject = "Activate your PlayNexus Playspace";
            smd.from = OurEmail;
            smd.content = "";
            smd.content += "PlayNexus Playspace name: " + csi.Name + "<br>";
            smd.content += "Owner: " + csi.Email + "<br>";
            smd.content += "<br>";
            smd.content += String.Format("Click this link to activate: <a href='{0}'>{1}</a>", href, href);
            GigForms.SendTheMail(mail, false);
        }

        private static void ValidateSpaceNameThrow(CreateSpaceItem csi)
        {
            validateEl = csi.NameEl;
            string txt = csi.Name;
            if (txt.Length < 4)
            {
                Ask.Ok(SpaceNameShortMsg);
                throw new Exception(GigForms.ValidateException);
            }
            if (txt.Length >= 20)
            {
                Ask.Ok(SpaceNameLongMsg);
                throw new Exception(GigForms.ValidateException);
            }
            if (txt.ToLowerCase() != txt)
            {
                Ask.Ok(SpaceNameLowerMsg);
                throw new Exception(GigForms.ValidateException);
            }
            if (!((RegularExpression) Script.Literal(@"/^[a-z][a-z0-9_]+$/")).Test(txt))
            {
                Ask.Ok(SpaceNameLettersMsg);
                throw new Exception(GigForms.ValidateException);
            }
        }

        private static void ValidateEmailAddrThrow(CreateSpaceItem csi)
        {
            validateEl = csi.EmailEl;
            if (!Rx.EmailFilter.Test(csi.Email))
            {
                Ask.Ok(EmailValidMsg);
                throw new Exception(GigForms.ValidateException);
            }
        }

        private static void ValidatePwThrow(CreateSpaceItem csi)
        {
            validateEl = csi.Password1El;
            if (csi.Password1.Length < 8)
            {
                GigForms.SetDeef(csi.Password1El);
                GigForms.SetDeef(csi.Password2El);
                Ask.Ok(PasswordLengthMsg);
                throw new Exception(GigForms.ValidateException);
            }
        }

        private static void CreateMath()
        {
            jQueryObject mpEl = jQuery.Select("#MathProblem");
            jQueryObject mrEl = jQuery.Select("#MathResult");
            int r;
            string ot;
            do
            {
                int m1 = Formulas.RandomOf(9);
                int m2 = Formulas.RandomOf(9);
                int m3 = Formulas.RandomOf(9);
                string o1 = Formulas.RandomOf(1) == 0 ? "+" : "+";
                string o2 = Formulas.RandomOf(1) == 0 ? "+" : "+";
                r = m1;
                r = o1 == "+" ? r + m2 : r - m2;
                r = o2 == "+" ? r + m3 : r - m3;
                ot = String.Format("{0} {1} {2} {3} {4} =", m1, o1, m2, o2, m3);
            } while (r < 0);
            mrEl.Data("mr", r);
            mpEl.Html(ot);
        }

        private static void ValidateMath()
        {
            validateEl = jQuery.Select("#MathResult");
            jQueryObject mrEl = jQuery.Select("#MathResult");
            int mr = (int) mrEl.GetDataValue("mr");
            GigForms.ValidateThrow(mrEl);
            int result = Number.Parse(mrEl.GetValue());
#if DEBUG
            if (result == -9999 && (bool) Storage.GetLocal(ThemeBase.DevServerKey))
                return;
#endif
            if (result != mr)
            {
                CreateMath();
                GigForms.SetDeef(mrEl);
                Ask.Ok(IncorrectMathMsg);
                throw new Exception(GigForms.ValidateException);
            }
        }

        private static void VerifySpaceAvailableAw(Await awp, string name)
        {
            validateEl = jQuery.Select("#CreateButton");
            new Await()
                .AddAw(CkSpaceAvailAw, name)
                .HandleDl(Exceptions.BlobLoadError, Await.RethrowAw)
                .AddDx(delegate(Await aw)
                       {
                           bool ic = (bool) aw.Result;
                           if (!ic)
                               Ask.Ok(String.Format("The space \"{0}\" is not available", name));
                       })
                .Commit(awp);
        }

        private static void CkSpaceAvailAw(Await awp, string name)
        {
            Dictionary args = new Dictionary(
                "name", name
                );

            jQueryAjaxOptions options = new jQueryAjaxOptions();
            options.Url = Uri.App("ph/file.ckcreate.php");
            options.DataType = "json";
            options.Data = args;
            options.Type = "GET";

            options.Success =
                delegate(object data, string textStatus, jQueryXmlHttpRequest request1)
                {
                    Dictionary result = (Dictionary) data;
                    awp.Result = result["result"];
                    awp.Done();
                };

            options.Error =
                delegate(jQueryXmlHttpRequest request, string textStatus, Exception error)
                {
                    Inform.Error(String.Format("Error verifying space {0}, {1} {2}", name, textStatus, error));
                    awp.Handle(new Exception(Exceptions.BlobLoadError));
                };
            jQuery.Ajax(options);
        }

        public static void CreateSpaceAw(Await awp, CreateSpaceItem csi)
        {
            int start = (new Date()).GetTime();
            string msg = String.Format(" - creating space name={0}", csi.Name);
            Inform.Event("Site.CreateSpaceAw", msg);
            Exceptions.SaveLog("build", "beginning" + msg);

            Cluster.Spinner(true);

            csi.CreateTime = (new Date()).GetTime();
            Uri.BumpVersion(); // So that testing always gets fresh libs

            Dictionary args = new Dictionary(
                "spaceName", csi.Name,
                "curatePassword", csi.Password1,
                "curateEmail", csi.Email
                );

            jQueryAjaxOptions options = new jQueryAjaxOptions();
            options.Url = Uri.App("ph/createspace.php");
            options.DataType = "json";
            options.Data = args;
            options.Type = "GET";
#if DEBUG
            options.Url += "?u=" + (new Date()).GetTime();
#endif

            options.Success =
                delegate(object data, string textStatus, jQueryXmlHttpRequest request1)
                {
                    PkgResult pr = (PkgResult) data;

                    if (pr != null && pr.result)
                    {
                        // TODO: Plug hole whereby activateId is sent back to user by handling wholely in PHP by emailing from createspace.php directly
                        csi.ActivateId = pr.activateId;
                        SendThemMail(csi);
                        SendMeMail(csi);
                        string host = Uri.Front(Window.Location.Href);
                        host = host.Substr(0, host.Length - "creator/pages".Length) + "/spaces";

                        // Show the log
                        //foreach (KeyValuePair<string, string> p in pr.log)
                        //{
                        //    Inform.Debug("Log: {0} = {1}", p.Key, p.Value);
                        //}

                        // Rebuild the javascript package
                        string url = String.Format(Uri.Join(ThemeBase.RootPath, "spaces/{0}/Scripts/{1}?minimize=y"), csi.Name, ThemeYoshi.MasterScripts);
                        //Storage.RemoveLocal("LoadLog");
                        new Await()
                            .LoadScript(Uri.Uniqueify(url))
                            //.WaitDx(delegate { return Script.IsValue(Storage.GetLocal("LoadLog")); })
                            .AddDx(delegate { Cluster.Spinner(false); })
                            .AddDx(delegate { Exceptions.SaveLog("build", String.Format("finished{0} time={1}ms", msg, (new Date()).GetTime() - start)); })
                            .AddAw(new Ask().OkAw, String.Format("Created new space \"{0}\"<br><br><span style='font-size:60%'>{1}</span><br><br>You will receive an email with an activation code for your new space.", csi.Name, Uri.Join(host, csi.Name, ThemeYoshi.MasterPage)))
                            .Commit(awp);
                        return;
                    }
                    Cluster.Spinner(false);
                    awp.Done();
                };

            options.Error =
                delegate(jQueryXmlHttpRequest request, string textStatus, Exception error)
                {
                    Cluster.Spinner(false);
                    Inform.Error(String.Format("Error creating space {0}, {1} {2}", csi.Name, textStatus, error));
                    awp.Handle(new Exception(Exceptions.BlobLoadError));
                };
            jQuery.Ajax(options);
        }
    }

    [IgnoreNamespace]
    public sealed class CreateSpaceItem
    {
        public string ActivateId;
        public int CreateTime;
        public string Email;
        public jQueryObject EmailEl;
        public string Name;
        public jQueryObject NameEl;
        public string Password1;
        public jQueryObject Password1El;
        public string Password2;
        public jQueryObject Password2El;
    }
}